Release 10.1A: OpenEdge Development:
Web Services


Modifying a reused SOAP response header

This example shows how you might handle a SOAP header that you first encounter in the response message returned from the Web service, then modify it as the SOAP header for the next request. The response header and its handler are identical to what is used in the header reuse example (see the "Reusing an unchanged SOAP response header" section). This is an example of the header returned from the Web service after a password is added:

SOAP request header built from a modified SOAP response header
<SOAP-ENV:Envelope 
        . . . 
        <SOAP-ENV:Header> 
                <ns0:AuthHeader xmlns:ns0="http://ServiceHost/SOAPHeader"> 
                        <ns0:AccessID>XYZZY</ns0:AccessID> 
                        <ns0:Password>Administrator</ns0:Password>  
                </ns0:AuthHeader> 
        </SOAP-ENV:Header> 
        . . . 
</SOAP-ENV:Envelope> 

Note that the client adds the <Password> element as a sibling to the <AccessID> element in the existing AuthHeader header entry. Another approach is to create and add a new Password header entry as a sibling to the AuthHeader header entry itself. Again, the actual approach depends on the Web service itself, in this case the HeaderExample Web service. This type of header modification might be used when the Web service and client maintain a consistent context for each other between requests and the operation involved requires authorization or authentication or some other additional context information.

Following is the mainline of a procedure that invokes the Web service to reuse the initial SOAP response header by adding a password node to it before passing it back as a SOAP request header. This code:

  1. Defines several mainline variables, including handles to access the global reused SOAP header (g_header) and its XML, and a variable to hold the password value (cPassword).
  2. Registers the request header (ReqHandler) and response header (RespHandler) handlers after connecting to the Web service and instantiating the HeaderSoap port type procedure object.
  3. Runs the OpenAccess procedure to invoke the Web service operation that returns the AccessID value in the SOAP response header (see the "Response header handler for returning a header for reuse" section).
  4. Invoking a request that modifies a reused SOAP response header (start) 
    /* 
     * SOAPHeader2.p 
     * 
     * An addition to SOAPHeader1.p. 
     * 
     * Calls a ficticious Web service. 
     * The first operation (OpenAccess) sends nothing in the request headers and 
     * gets back a SOAP response header containing an AccessID. 
     * The second operation (HelloWorld) sends the AccessID back in its request  
     * header. (No additional information is received in the response header.) 
     * The third operation (HelloSecureWorld) adds a Password node to the  
     * existing AccessID entry in its request header. This Password node is added 
     * as a sibling of the AccessID element and NOT as a new SOAP header entry.  
     * (Once again no additional information is received in the response header.) 
     * 
     * The Web service has only one service and port available. 
     */ 
    /*1*/ 
    /* Define local variables */ 
    DEFINE VARIABLE hWebSrvc      AS HANDLE.  
    DEFINE VARIABLE hPortType     AS HANDLE.  
    DEFINE VARIABLE cResponse     AS CHARACTER FORMAT "x(72)".  
    DEFINE VARIABLE hXdoc         AS HANDLE.  
    DEFINE VARIABLE hXnoderef1    AS HANDLE.  
    DEFINE VARIABLE hXnoderef2    AS HANDLE.  
    DEFINE VARIABLE hXtext        AS HANDLE.  
    DEFINE VARIABLE cPassword     AS CHARACTER INIT ?.  
    DEFINE VARIABLE g_header      AS HANDLE.  
    /* Create the Web service server object */ 
    CREATE SERVER hWebSrvc. 
    /* Connect to the WS */ 
    hWebSrvc:CONNECT("-WSDL 
                      http://ServiceHost/SOAPHeader/HeaderExample.asmx?wsdl"). 
    /* Get the method, set the port type */ 
    RUN HeadersSoap SET hPortType ON hWebSrvc. 
    /*2*/ 
    /* Associate the req. & resp. callbacks with the port type */ 
    hPortType:SET-CALLBACK-PROCEDURE("REQUEST-HEADER", "ReqHandler"). 
    hPortType:SET-CALLBACK-PROCEDURE("RESPONSE-HEADER", "RespHandler"). 
    /*3*/ 
    /* Invoke the Web service with no header and display the results */ 
    RUN OpenAccess IN hPortType (OUTPUT cResponse).  
    DISPLAY cResponse LABEL "WS response" WITH FRAME aaa. 
    

    Invoking a request that modifies a reused SOAP response header (end) 
    /*4*/ 
    /* Go again with the AccessID set from previous response */ 
    cResponse = "". 
    RUN HelloWorld IN hPortType (OUTPUT cResponse).  
    DISPLAY cResponse LABEL "WS response" WITH FRAME bbb. 
    /*5*/ 
    /* Go again with the AccessID set from previous response */ 
    /* header together with an added Username and Password   */ 
    cResponse = "". 
    cPassword = "Administrator".  
    RUN HelloSecureWorld IN hPortType (OUTPUT cResponse).  
    DISPLAY cResponse LABEL "WS response" WITH FRAME ccc. 
    /*6*/ 
    DELETE OBJECT g_header.  
    DELETE OBJECT hPortType.  
    hWebSrvc:DISCONNECT().  
    DELETE OBJECT hWebSrvc.  
    /**************** Internal Procedures ****************/ 
    

  5. Runs the HelloWorld procedure to invoke the next Web service operation, passing back the SOAP response header to the Web service unchanged as the SOAP request header (see the "Request header handler for reusing and modifying a header" section).
  6. Runs the HelloSecureWorld procedure to invoke the next Web service operation, passing back the password-modified SOAP response header as the Web service SOAP request header (see the "Request header handler for reusing and modifying a header" section).
  7. Note: The header handler processing for Step 4 is different from Step 5, to reflect that the initial SOAP response header is unchanged for one request and modified for the next.

  8. Cleans up the global objects maintained in its context and disconnects from the Web service. Note that one of the objects it deletes is the original SOAP response header saved by the response header handler during execution of the OpenAccess procedure.
Request header handler for reusing and modifying a header

This is the SOAP request header handler (ReqHandler) that reuses the initial SOAP response header and adds a Password value to the existing AuthHead header entry to pass along with the AccessID value between the client and Web service:

Request header handler modifying a saved SOAP response header (start)
PROCEDURE ReqHandler: /*1*/  
    DEFINE OUTPUT PARAMETER hHeader    AS HANDLE. 
    DEFINE INPUT  PARAMETER cNamespace AS CHARACTER. 
    DEFINE INPUT  PARAMETER cLocalNS   AS CHARACTER. 
    DEFINE OUTPUT PARAMETER lDeleteOnDone AS LOGICAL. 
        /* The IF test determines if this is the first call  
           through this code. 
           If it is, then g_header is not set and hHeader is set 
           to ? to ensure that no header is sent. 
           g_header gets set when the response header is 
           returned, so a subsequent pass through this code takes 
           the previous response header and sends it as the 
           current request header, possibly modified to  
           authenticate a secure request. */ 
    IF NOT VALID-HANDLE (g_header) THEN DO: /*2a*/ /* first request */ 
        hHeader = ?.  
        lDeleteOnDone = TRUE.  
    END. 
    ELSE DO: /*2b*/ /* all subsequent requests */ 
        hHeader = g_header.  
        lDeleteOnDone = FALSE.  

This code:

  1. Sends the SOAP request header for the HelloWorld and HelloSecureWorld requests (and any request run after running OpenAccess).
  2. Tests if the global header handle (g_header) references a valid object:
    1. If it does not reference an object, the request handler must be running as part of the initial call to OpenAccess and sets the output parameters to ensure that no initial SOAP request header is sent.
    2. If it does reference an object, the handler passes the global header object as output using the request header parameter (hHeader) and ensures that the object is not deleted (saving it for use in any further request).
    3. Request header handler modifying a saved SOAP response header (end) 
              /* Password node data are added to the existing SOAP header  
                 if a secure operation is being executed */ 
      /*3*/   IF cPassword <> ? THEN DO:  
      /*3a*/      /* Build a new x-doc to contain the Password data */ 
                  CREATE X-DOCUMENT hXdoc. 
                  CREATE X-NODEREF  hXnoderef1. 
                  CREATE X-NODEREF  hXnoderef2. 
                  CREATE X-NODEREF  hXtext. 
                  /* Add the Password data as a child of the existing header entry */ 
      /*3b*/      DEFINE VARIABLE hHeaderEntryref AS HANDLE.  
                  DEFINE VARIABLE ClientNS AS CHARACTER 
                      INIT "http://ServiceHost/SOAPHeader". 
                  CREATE SOAP-HEADER-ENTRYREF hHeaderEntryref. 
      /*3c*/      hHeader:GET-HEADER-ENTRY(hHeaderEntryref, 1). 
                  hHeaderEntryref:GET-NODE(hXnoderef1). 
      /*3d*/      hXdoc:IMPORT-NODE(hXnoderef2, hXnoderef1, TRUE). 
                  IF hXnoderef2:NUM-CHILDREN > 1 THEN DO: /* Get rid of previous */ 
                      hXnoderef2:GET-CHILD(hXnoderef1,2). /* Password data     */ 
                      hXnoderef1:DELETE-NODE(). 
                  END. 
      /*3e*/      hXdoc:APPEND-CHILD(hXnoderef2). 
                  hXdoc:CREATE-NODE-NAMESPACE(hXnoderef1, ClientNS,  
                                              "Password", "ELEMENT"). 
      /*3f*/      hXnoderef2:APPEND-CHILD(hXnoderef1). 
                  hXdoc:CREATE-NODE(hXtext, "", "text").  
                  hXnoderef1:APPEND-CHILD(hXtext). 
                  hXtext:NODE-VALUE = cPassword.  
                  /* Replace the existing header entry using a deep copy of the */ 
                  /* new version from the x-doc that has the Password node added */ 
      /*3g*/      hHeaderEntryref:SET-NODE(hXnoderef2).  
                  /* Procedure/header cleanup */ 
      /*3h*/      cPassword = ?. /* Use current password until replaced */ 
                  DELETE OBJECT hHeaderEntryref. 
                  DELETE OBJECT hXdoc. 
                  DELETE OBJECT hXnoderef1. 
                  DELETE OBJECT hXnoderef2. 
                  DELETE OBJECT hXtext. 
              END. 
          END. /* all subsequent requests */ 
      END PROCEDURE. 
      

  3. Tests if a password has been specified for the current Web service request, indicated by any cPassword value that is not the Unknown value (?). If the current Web service request is nonsecure (as with the HelloWorld operation), all work has been done and the request handler can end. If the current Web service request is secure (as with the HelloSecureWorld operation), the request handler adds the password information to the SOAP request header, as follows:
  4. Note: After the first secure request, all future requests (secure or nonsecure) send a request header that includes password information because the password information is never deleted until replaced by a newly-specified password.

    1. Creates the XML x-document and x-noderef objects to manipulate the SOAP header.
    2. Creates the SOAP header entryref object (hHeaderEntryref) to access SOAP header entries and defines the namespace (ClientNS) used for defining the SOAP header entry for this request.
    3. Returns the existing header entry from the saved global SOAP header object using the GET-HEADER-ENTRY( ) method on the SOAP header object and accesses the XML root node of the entry using the GET-NODE( ) method on the SOAP header entryref object.
    4. Note: The handler is adding the password information to an existing SOAP header entry. If it was adding a new header entry to hold the information, it would invoke the ADD-HEADER-ENTRY( ) method to add the header entry to contain the new XML for it.

    5. Imports the header entry root node into an x-document object in order to access and modify the XML for the header entry, and also deletes any password data from a previous secure request before adding the currently-specified password data.
    6. Adds the <Password> element as a sibling of the <AccessID> element.
    7. Adds the <Password> element value.
    8. Replaces the entire existing header entry in the global SOAP header object with the header entry updated in the x-document object.
    9. Sets the password value (cPassword) to unknown (?), which retains the current password in the header entry until it is explicitly changed in cPassword. Deletes all of the helper XML and SOAP header entryref objects created in the header handler.

Copyright © 2005 Progress Software Corporation
www.progress.com
Voice: (781) 280-4000
Fax: (781) 280-4095